package vertx.starter.services.http;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpServer;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.jdbc.JDBCAuth;
import io.vertx.ext.jdbc.JDBCClient;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.AuthHandler;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CookieHandler;
import io.vertx.ext.web.handler.FormLoginHandler;
import io.vertx.ext.web.handler.RedirectAuthHandler;
import io.vertx.ext.web.handler.SessionHandler;
import io.vertx.ext.web.handler.UserSessionHandler;
import io.vertx.ext.web.sstore.LocalSessionStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import vertx.starter.services.db.DatabaseService;

import java.util.List;

import static vertx.starter.conf.DatabaseConstants.CONFIG_WIKIDB_JDBC_DRIVER_CLASS;
import static vertx.starter.conf.DatabaseConstants.CONFIG_WIKIDB_JDBC_MAX_POOL_SIZE;
import static vertx.starter.conf.DatabaseConstants.CONFIG_WIKIDB_JDBC_URL;
import static vertx.starter.conf.DatabaseConstants.DEFAULT_JDBC_MAX_POOL_SIZE;
import static vertx.starter.conf.DatabaseConstants.DEFAULT_WIKIDB_JDBC_DRIVER_CLASS;
import static vertx.starter.conf.DatabaseConstants.DEFAULT_WIKIDB_JDBC_URL;

/**
 * there is sub route testing with failed.
 */
public class HttpServerVerticle extends AbstractVerticle {

    private static final
    Logger LOGGER = LoggerFactory.getLogger(HttpServerVerticle.class);

    public static final String CONFIG_HTTP_SERVER_PORT = "http.server.port";
    public static final String CONFIG_WIKIDB_QUEUE = "wikidb.queue";

    private DatabaseService dbService;

    @Override
    public void start(Promise<Void> promise) throws Exception {

        String wikiDbQueue = config().getString(CONFIG_WIKIDB_QUEUE, "wikidb.queue");
        dbService = DatabaseService.createProxy(vertx, wikiDbQueue);

        Router router = Router.router(vertx);
        router.route().handler(BodyHandler.create());

        // addAuthConfig(router);
        router.get("/page").handler(this::queryHandler);
        router.post("/create").handler(this::createHandler);
        router.delete("/").handler(this::deleteHandler);
        router.mountSubRouter("/api", router);

        int port = config().getInteger(CONFIG_HTTP_SERVER_PORT, 8089);
        HttpServer server = vertx.createHttpServer();
        server
                .requestHandler(router)
                .listen(port, ar -> {
                    if (ar.succeeded()) {
                        LOGGER.info("HTTP server running on port: " + port);
                        promise.complete();
                    } else {
                        LOGGER.error("Could not start a HTTP server", ar.cause());
                        promise.fail(ar.cause());
                    }
                });

    }

    private void addAuthConfig(Router router) {
        router.route().handler(CookieHandler.create());
        router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));
        JDBCAuth authProvider = authClient();
        router.route().handler(UserSessionHandler.create(authProvider));
        AuthHandler authHandler = RedirectAuthHandler.create(authProvider, "/login");

        router.patch("/**").handler(authHandler);

        router.get("/login").handler(this::loginHandler);
        router.post("/login-auth").handler(FormLoginHandler.create(authProvider));
    }

    private void loginHandler(RoutingContext routingContext) {
        routingContext.response().end("please login!");
    }

    private void apiRoot(RoutingContext context) {
        dbService.fetchAllPages(reply -> {
            JsonObject response = new JsonObject();
            if (reply.succeeded()) {
                JsonArray pages = reply.result();
                response
                        .put("success", true)
                        .put("pages", pages);
                context.response().setStatusCode(200);
                context.response().putHeader("Content-Type", "application/json");
                context.response().end(response.encode());
            } else {
                response
                        .put("success", false)
                        .put("error", reply.cause().getMessage());
                context.response().setStatusCode(500);
                context.response().putHeader("Content-Type", "application/json");
                context.response().end(response.encode());
            }
        });
    }

    private void deleteHandler(RoutingContext routingContext) {

        String name = null;
        List<String> stringList = routingContext.queryParam("name");
        if (stringList != null && !stringList.isEmpty())
            name = stringList.get(0);
        dbService.deletePage(name, relpy -> {
            if (relpy.succeeded()) {
                Integer result = relpy.result();
                routingContext.response().end(result.toString());

            } else {
                routingContext.fail(relpy.cause());
            }

        });
    }

    private void createHandler(RoutingContext context) {
        JsonObject bodyAsJson = context.getBodyAsJson();
        Handler<AsyncResult<Integer>> handler = reply -> {
            if (reply.succeeded()) {
                context.response().setStatusCode(200);
                context.response().end(bodyAsJson.toBuffer());
            } else {
                context.fail(reply.cause());
            }
        };


        dbService.createPage(bodyAsJson.getString("name"), bodyAsJson.getString("content"), handler);
    }

    private void queryHandler(RoutingContext context) {
        dbService.fetchAllPages(reply -> {
            if (reply.succeeded()) {
                JsonArray body = reply.result();
                context.response().putHeader("Content-Type", "application/json");
                context.response().end(body.toString());

            } else {
                context.fail(reply.cause());
            }
        });
    }

    public JDBCAuth authClient(){
        JDBCClient dbClient = JDBCClient.createShared(vertx, new JsonObject()
                .put("url", config().getString(CONFIG_WIKIDB_JDBC_URL, DEFAULT_WIKIDB_JDBC_URL))
                .put("driver_class", config().getString(CONFIG_WIKIDB_JDBC_DRIVER_CLASS, DEFAULT_WIKIDB_JDBC_DRIVER_CLASS))
                .put("max_pool_size", config().getInteger(CONFIG_WIKIDB_JDBC_MAX_POOL_SIZE, DEFAULT_JDBC_MAX_POOL_SIZE)));

        return JDBCAuth.create(vertx, dbClient);
    }
}
